
#include <BRender>
#include <BLight>
#include <BPainter>
#include <BEvent>
#include <BMouseEvent>

#include <BViewer>
#include "member_BRender.h"
#include "member_BViewer.h"
#include "member_BWidget.h"
#include "member_BHandler.h"
#include "member_BCamera.h"
#include "member_BNode.h"
#include "member_BScene.h"
#include "member_BPicker.h"

using namespace BWE;

#define member					(*(member_BViewer*)_ptr)
#define member_allocate()		_ptr = new member_BViewer(this)
#define member_release()		delete (member_BViewer*)_ptr

BViewer::BViewer(BWidget* parent) : BWidget(parent)
{
	member_allocate();
	this->setSizeStretch(5);
}
BViewer::~BViewer()
{
	member_release();
}

void BViewer::setScene(BScene* scene)
{
	if (member.scene != scene)
	{
		if (member.scene)
			member.disconnect(member.scene);
		member.scene = scene;
		if (member.scene)
			member.connect(member.scene, Signal_Fresh, &member_BViewer::slotSceneFresh);
	}
}
BScene* BViewer::scene() const
{
	return member.scene;
}

void BViewer::setCamera(BCamera* camera)
{
	if (member.camera != camera)
	{
		member.camera = camera;
		BRect crect = this->clientRect();
		member.camera->setPixelSize(crect.size());
		double aspect = (double)crect.width() / (double)crect.height();
		member.camera->setAspect(aspect);
		this->fresh();
	}
}
BCamera* BViewer::camera()
{
	return member.camera.ptr();
}
const BCamera* BViewer::camera() const
{
	return member.camera.ptr();
}

void BViewer::setHandler(BHandler* handler)
{
	if (member.handler != handler)
	{
		if (member.handler)
			handler_member(member.handler)->setViewer(0);
		member.handler = handler;
		if (member.handler)
			handler_member(member.handler)->setViewer(this);
		this->fresh();
	}
}
BHandler* BViewer::handler()
{
	return member.handler;
}
const BHandler* BViewer::handler() const
{
	return member.handler;
}

double BViewer::unit(double dist) const
{
	if (!member.camera)
		return false;

	BRect crect = this->clientRect();

	double cleft = member.camera->left();
	double cright = member.camera->right();
	double cnear = member.camera->near();
	double hw = crect.width() * 0.5;
	double chw = (cright - cleft) * 0.5;
	double offsetX =  chw / hw;
	//xunit = offsetX * d / cnear;

	//double ctop = member.camera->top();
	//double cbottom = member.camera->bottom();
	//double hh = crect.height() * 0.5;
	//double chh = (ctop - cbottom) * 0.5;
	//double offsetY =  chh / hh;
	//yunit = offsetY * d / cnear;

	return offsetX * dist / cnear;
}
double BViewer::unit(const BVector& point) const
{
	if (!member.camera)
		return 1.0;
	double dist = (member.camera->position() - point).length();
	return unit(dist);
}

BVector BViewer::vertex(int x, int y, double d) const
{
	if(!member.camera)
		return BVector();
	BVector position = member.camera->position();
	BVector center = member.camera->center();
	BVector up = member.camera->up();
	double cleft = member.camera->left();
	double cright = member.camera->right();
	double ctop = member.camera->top();
	double cbottom = member.camera->bottom();
	double cnear = member.camera->near();

	BVector direct = center - position;
	direct.normalize();

	BVector right = direct.cross(up);
	right.normalize();

	up = right.cross(direct);
	up.normalize();

	double chw = (cright - cleft) * 0.5;
	double chh = (ctop - cbottom) * 0.5;

	BRect crect = this->clientRect();
	x -= crect.x();
	y -= crect.y();

	double hw = crect.width() * 0.5;
	double hh = crect.height() * 0.5;

	double offsetX = (x - hw) / hw * chw;
	double offsetY = (y - hh) / hh * chh;

	right *= offsetX * d / cnear;
	up *= offsetY * d / cnear;

	BVector base = position + direct * d;
	return base + right - up;
}
BLine BViewer::line(int x, int y) const
{
	if(member.camera)
	{
		BVector start = member.camera->position();
		BVector end = vertex(x, y, member.camera->far());
		return BLine(start, end);
	}
	return BLine();
}

BVector BViewer::vertex(const BPoint& point, double d) const
{
	return vertex(point.x(), point.y(), d);
}
BLine BViewer::line(const BPoint& point) const
{
	return line(point.x(), point.y());
}

const BPickItem* BViewer::pick(int x, int y) const
{
	if (!member.camera || !member.scene || member.scene->empty())
		return 0;
	BCamera* camera = member.camera;
	if (member.picker == 0)
	{
		member.picker = new BPicker();
	}
	BPicker* picker = member.picker;

	BLine line = this->line(x, y);
	member.picker->setLine(line);

	const ShapeInfoArray& shapeArray = camera_member(camera)->shapeArray;
	for (int i = 0; i < shapeArray.size(); i++)
	{
		const ShapeInfo& sinfo = shapeArray[i];
		BLine localLine = line;
		if (sinfo.node)
		{
			BMatrix matrix = sinfo.node->matrix();
			matrix.inverse();
			localLine = line * matrix;
			picker_member(picker)->node = sinfo.node;
		}
		picker_member(picker)->pick(localLine, sinfo.shape);
	}
	const ShapeInfoQueue& shapeQueue = camera_member(camera)->transShapeQueue;
	for (auto it = shapeQueue.begin(); it.valid(); it++)
	{
		const ShapeInfo& sinfo = *it;
		BLine localLine = line;
		if (sinfo.node)
		{
			BMatrix matrix = sinfo.node->matrix();
			matrix.inverse();
			localLine = line * matrix;
			picker_member(picker)->node = sinfo.node;
		}
		picker_member(picker)->pick(localLine, sinfo.shape);
	}
	return picker_member(picker)->items.first();
}
const BPickItem* BViewer::pick(const BPoint& point) const
{
	return pick(point.x(), point.y());
}

BSize BViewer::sizeHint() const
{
	BSize size = BWidget::sizeHint();
	if (size.width() < 100)
		size.setWidth(100);
	if (size.height() < 100)
		size.setHeight(100);
	return size;
}

void BViewer::event(const BEvent& event)
{
	BWidget::event(event);
	if (member.handler)
	{
		//handler_member(member.handler)->event(event);
		member.freshHandler(member.handler, event);
	}
	if (event.type() == Event_Update)
	{
		if (member.camera && camera_member(member.camera)->refresh)
		{
			member.camera->emit(Signal_Fresh);
			this->fresh();
		}
	}
}

void BViewer::resizeEvent(const BEvent& event)
{
	if (member.camera)
	{
		BRect crect = this->clientRect();
		member.camera->setPixelSize(crect.size());
		double aspect = (double)crect.width() / (double)crect.height();
		member.camera->setAspect(aspect);
		this->fresh();
	}
}

void BViewer::freshEvent(const BEvent& event)
{
	if (member.camera)
	{
		member.camera->fresh();
	}
}
void BViewer::paintEvent(const BEvent& event)
{
	BWidget::paintEvent(event);

	if (member.camera && member.scene)
	{
		BArray<BCamera*>& cameras = scene_member(member.scene)->cameras;
		for (int i = 0; i < cameras.size(); i++)
		{
			BCamera* camera = cameras[i];
			if (camera != member.camera)
			{
				camera_member(camera)->frame(member.scene);
			}
		}
		camera_member(member.camera)->frame(member.scene, member.handler);
	}
	const BRect& crect = this->clientRect();
	int x = crect.x();
	int y = crect.y();
	int width = crect.width();
	int height = crect.height();

	BPainter painter(this);

	painter.bind(member.camera);
	glBegin(GL_QUADS);
		glTexCoord2f(0.0f, 1.0f);		glVertex2i(x, y);
		glTexCoord2f(0.0f, 0.0f);		glVertex2i(x, y + height);
		glTexCoord2f(1.0f, 0.0f);		glVertex2i(x + width, y + height);
		glTexCoord2f(1.0f, 1.0f);		glVertex2i(x + width, y);
	glEnd();
	painter.unbind();

	member.paintHandler(member.handler, painter);
	painter.drawBorder();
	painter.drawPerch();
	painter.drawMargin();
}
